#ifndef __CONNECTION_HPP__
#define __CONNECTION_HPP__

/* Connection模块：用户态的连接。是对Socket模块、Buffer模块、Channel模块的整合。
    还有对通信套接字上的事件就绪的回调函数。
    1. 当主线程获取到一个新的通信连接后，就为这个新的通信连接创建一个Connection对象（即用户态连接）来管理这个通信连接。
    2. 为用户态连接设置回调函数：连接建立成功回调、接收到信息回调、连接关闭回调、连接上任意事件产生回调。
    3. 为通信套接字设置回调函数：读事件触发回调、写事件触发回调、套接字关闭回调、错误事件回调、任意事件回调。
    4. 主线程可能会监听获取到多通信连接，这样就有多个用户态连接，所以要管理这些用户态连接。所以，通信套接字关闭回调函数中最后，
    应该移除服务器对该用户态连接的管理，所以还要有这个的回调函数。
    5. 发送数据。
    6. 启动关闭非活跃连接超时销毁功能
    7. 切换连接连接支持的应用层协议

    因此，Connection模块应维护以下信息：
    用户态连接id、用户态连接的状态、通信套接字Socket对象、对Socket上事件监控管理的Channel对象、
    连接所绑定的EventLoop句柄（对象由主线程创建）、发送/接收缓冲区、切换协议需要的协议上下文、各种回调函数
 */

static const int tmp_buf_size = 65536;// 临时缓冲区大小
class Connection;
using ConnectionPtr = std::shared_ptr<Connection>;// Connection智能指针对象类型定义

/**
 * @brief 连接状态
 * 
 */
typedef enum
{
    CONNECTING,// 连接就绪——这个状态阶段是完成连接连接建立后的必要准备工作
    CONNECTED,// 已连接——这个状态就代表连接已经完全建立，可以进行通信
    DISCONNECTING,// 连接关闭就绪——这个状态阶段表示正在关闭连接，进行真正关闭连接前的必要工作
    DISCONNECTED// 连接已关闭——这个状态表示连接已真正关闭
}ConnStatus;

class Connection : public std::enable_shared_from_this<Connection>
{
    using ConnectedCallback = std::function<void(const ConnectionPtr&)>;// 连接建立的回调函数类型定义
    using ClosedCallback = std::function<void(const ConnectionPtr&)>;// 连接关闭的回调函数类型定义
    using MessageCallback = std::function<void(const ConnectionPtr&, Buffer*)>;// 连接有数据到来的回调函数类型定义
    using EventCallback = std::function<void(const ConnectionPtr&)>;// 连接有任意事件被触发的回调函数类型定义
    using SvrClosedCallback = std::function<void()>;// 从外部移除对当前连接管理的回调函数类型定义
private:
    uint64_t _conn_id;// 连接id
    uint64_t _timertask_id;// 定时任务id
    ConnStatus _conn_status;// 连接状态，默认为CONNECTING
    Socket _sock;// 通信套接字
    int _sockfd;// 仅仅用来记录Connection管理的套接字，方便在套接字关闭后仍然可以知道管理Connection管理的什么套接字，\
                方便日志打印，后期可考虑移除
    EventLoop* _eventloop;// 连接绑定的loop的句柄
    Channel _channel;// 对通信套接字进行事件监控的管理
    Any _context;// 上下文
    bool _enable_idle_release;// 非活跃连接释放标志
    Buffer _inbuffer;// 用户态接收缓冲区
    Buffer _outbuffer;// 用户态发送缓冲区

    ConnectedCallback _connected_cb;// 用户态连接建立成功的回调函数
    ClosedCallback _closed_cb;// 用户态连接关闭后的回调函数
    MessageCallback _message_cb;// 用户态连接上信息到来的回调函数
    EventCallback _event_cb;// 用户态连接上任意事件产生的回调函数
    ClosedCallback _svrclosed_cb;// 移除对Connection管理的回调函数
private:
    /* 建立用户态连接Connection前的准备工作 */
    void ConnectionEstablishedInThreadLoop()
    {
        assert(_conn_status == CONNECTING);
        // 1. 设置连接状态为CONNECTED
        _conn_status = CONNECTED;
        // 2. 对通信套接字启动读事件监控
        _channel.EnableRead();
        // 3. 调用连接建立的回调函数
        if(_connected_cb) _connected_cb(shared_from_this());
    }
    /* 真正关闭连接 */
    void ReleaseInLoopThread()
    {
        // 1. 将连接状态改为DISCONNECTED，表示已关闭连接
        _conn_status = DISCONNECTED;
        // 2. 取消对通信套接字的事件监控
        _channel.RemoveEvents();
        // 3. 关闭通信套接字
        _sock.Close();
        // 4. 移除该连接的定时销毁任务
        // if(_eventloop->HasTimerTask(_timertask_id)) _eventloop->CancelTimerTask(_timertask_id);
        if(_eventloop->HasTimerTask(_timertask_id)) DisableIdleReleaseInLoopThread();// 还需要置连接超时释放标志为false
        // 5. 调用Connection连接关闭的回调函数
        if(_closed_cb) _closed_cb(shared_from_this());
        // 6. 移除对Connection的管理
        if(_svrclosed_cb) _svrclosed_cb(shared_from_this());
    }
    /* 预关闭连接——做关闭连接前的准备工作，处理缓冲区中的数据 */
    void CloseInLoopThread()
    {
        // 1. 将连接状态变为DISCONNECTING，表示正在关闭连接
        _conn_status = DISCONNECTING;
        // 2. 处理接收缓冲区中的待处理的数据
        if(_inbuffer.ReadAbleSize() > 0)
        {
            if(_message_cb) _message_cb(shared_from_this(), &_inbuffer);
        }
        // 3. 处理发送缓冲区中的数据
        if(_outbuffer.ReadAbleSize() > 0)
        {
            if(_channel.MonitorWrite() == false) _channel.EnableWrite();
        }
        if(_outbuffer.ReadAbleSize() == 0)
        {
            /* 当服务器在一次业务处理中花费了很长的时间(超过非活跃连接释放的超时时间)，就可能导致其他的连接被连累导致超时，
             * 这些被连累超时的连接就可能被释放。
             * 假设目前有4个描述符sock1,sock2,sock3,sock4，都已就绪，如果处理sock1上的事件的事件过长，导致sock2，sock3，sock4超时。
             * 1. 若sock2~4都是通信连接，那么当sock1上的事件处理完成后，sock2~4都超时，但直接执行相应的定时任务即可，然后将连接释放，
             * 并不会影响什么
             * 2. 但若sock2是定时器描述符，这时候就定时器超时，就可能会将sock3~4释放掉，关闭sock3~4的连接，
             * 此后在处理处理sock3~4上的事件时(如刷新活跃度)就会造成非法访问，所以，这里关闭连接不能直接关闭，
             * 而是将“关闭连接”当作任务押入到线程的任务池中
             */
            Release();
            // ReleaseInLoopThread();// 只要发送缓冲区中没有数据，就视为关闭连接前的准备工作已完成。\
            因为，接收缓冲区中的数据可能不完整，就导致接收缓冲区中的数据总是不被处理，所以无需再关注发送缓冲区
        }
    }

    /* 启动/关闭非活跃连接超时释放 */
    void EnableIdleReleaseInLoopThread(uint32_t timeout)
    {
        // 1. 设置非活跃连接超时销毁标志
        _enable_idle_release = true;
        // 2. 添加定时任务
        if(_eventloop->HasTimerTask(_timertask_id) == false)
        {
            _eventloop->AddTimerTask(_timertask_id, std::bind(&Connection::Release, this), timeout);
            // 超时销毁就不需要考虑缓冲区中的数据了，因此使用Release而非CloseInLoopThread
        }
    }
    void DisableIdleReleaseInLoopThread()
    {
        // 1. 取消设置非活跃连接超时销毁标志
        _enable_idle_release = false;
        // 2. 取消定时销毁任务
        if(_eventloop->HasTimerTask(_timertask_id)) _eventloop->CancelTimerTask(_timertask_id);
    }

    /* 发送数据 */
    void SendInLoopThread(const std::string& in)
    {
        if(_conn_status == DISCONNECTED) return;

        // 1. 将数据放到发送缓冲区
        // DBG_LOG("SendInLoopThread:Write: %ld", in.size());
        _outbuffer.WriteString(in);
        // 2. 启动通信套接字的写事件监控
        if(_channel.MonitorWrite() == false) _channel.EnableWrite();
    }

    /* 切换支持的应用层协议——切换协议就是切换上下文和回调函数 */
    void UpdateProtocolInThreadLoop(const Any& context, 
                                    const ConnectedCallback& connected_cb, 
                                    const ClosedCallback& closed_cb,
                                    const MessageCallback& message_cb,
                                    const EventCallback& event_cb)
    {
        _context = context;
        _connected_cb = connected_cb;
        _closed_cb = closed_cb;
        _message_cb = message_cb;
        _event_cb = event_cb;
    }

    /* 通信套接字上事件被触发的回调函数 */
    void HandleRead()
    {
        // 1. 接收通信套接字中的数据放到用户态接收缓冲区中
        char buf[tmp_buf_size] = { 0 };
        ssize_t n = _sock.NonBlockRecv(buf, tmp_buf_size - 1);
        if(n < 0)// 读取数据失败就关闭连接
        {
            // DBG_LOG("出现错误，Call CloseInLoopThread()");
            return CloseInLoopThread();// 预关闭连接
        }
        // DBG_LOG("HandleRead:Write: %ld", n);
        _inbuffer.Write(buf, n);
        // 2. 接收缓冲区中有数据，就调用数据到来的回调函数
        if(_inbuffer.ReadAbleSize() > 0 && _message_cb) _message_cb(shared_from_this(), &_inbuffer);
    }
    void HandleWrite()
    {
        // 1. 发送数据，将发送缓冲区中的数据“发送”到通信套接字的发送缓冲区
        ssize_t n = _sock.NonBlockSend(_outbuffer.ReadPosition(), _outbuffer.ReadAbleSize());
        if(n < 0)// 发送失败就关闭连接
        {
            // 关闭连接前，处理接收缓冲区中的数据
            if(_inbuffer.ReadAbleSize() > 0 && _message_cb) _message_cb(shared_from_this(), &_inbuffer);
            
            // DBG_LOG("出现错误，Call Release()");
            return Release();
        }
        _outbuffer.MoveReadOffset(n);
        // DBG_LOG("HandleWrite:已发送数据大小：%ld", n);
        // DBG_LOG("HandleWrite:移除已发送的数据: %ld", _outbuffer.ReadAbleSize());
        // 2. 发送缓冲区中没数据，就关闭写事件监控
        if(_outbuffer.ReadAbleSize() == 0)
        {
            _channel.DisableWrite();

            if(_conn_status == DISCONNECTING) // ReleaseInLoopThread();// 在预关闭连接ClosedInThreadLoop()中，如果发送缓冲区中有数据，\
                就会启动通信套接字的写事件监控，然后在预关闭连接ClosedInThreadLoop()结束，并没有直接关闭连接：ReleaseInLoopThread()。\
                启动通信套接字的写事件监控后就会执行HandleWrite()，最后终于发送缓冲区中没数据，\
                （如果连接处在预关闭状态）此时再关闭连接：ReleaseInLoopThread()。
                Release();
        }
    }
    void HandleClosed()
    {
        // 通信套接字已经关闭，只关心接收缓冲区即可
        if(_inbuffer.ReadAbleSize() > 0 && _message_cb) _message_cb(shared_from_this(), &_inbuffer);
        // 关闭连接
        // ReleaseInLoopThread();
        Release();
    }
    void HandleError()
    {
        DBG_LOG("there is a error event with this connection (socket)");
        HandleClosed();
    }
    void HandleEvent()
    {
        // 刷新连接活跃度
        if(_enable_idle_release & _eventloop->HasTimerTask(_timertask_id)) _eventloop->RefreshTimerTask(_timertask_id);
        if(_event_cb) _event_cb(shared_from_this());
    }

    /* 释放连接 */
    void Release()
    {
        _eventloop->RunTaskInLoopThread(std::bind(&Connection::ReleaseInLoopThread, this));
    }
public:
    Connection(uint64_t conn_id, EventLoop* eventloop, int sockfd): 
        _conn_id(conn_id), _timertask_id(_conn_id) , _conn_status(CONNECTING), 
        _sock(sockfd), _eventloop(eventloop), _channel(sockfd, _eventloop), _enable_idle_release(false), _sockfd(sockfd)
    {
        // 为通信套接字设置事件回调函数
        _channel.SetReadCallback(std::bind(&Connection::HandleRead, this));
        _channel.SetWriteCallback(std::bind(&Connection::HandleWrite, this));
        _channel.SetClosedCallback(std::bind(&Connection::HandleClosed, this));
        _channel.SetErrorCallback(std::bind(&Connection::HandleError, this));
        _channel.SetEventCallback(std::bind(&Connection::HandleEvent, this));
    }

    /* 获取连接管理的套接字 */
    int Fd() const
    {
        return _sock.Fd();
    }

    /* 获取连接管理的套接字(可能已被释放)。仅用来日志打印 */
    int TmpFd() const
    {
        return _sockfd;
    }

    /* 获取用户态连接id */
    uint64_t Id() const
    {
        return _conn_id;
    }

    /* 为用户态连接设置回调函数 */

    /* 注册连接建立成功的回调函数 */
    void SetConnectedCallback(const ConnectedCallback& cb)
    {
        _connected_cb = cb;
    }
    /* 注册连接关闭的回调函数 */
    void SetClosedCallback(const ClosedCallback& cb)
    {
        _closed_cb = cb;
    }
    /* 注册连接上有数据到来的回调函数 */
    void SetMessageCallback(const MessageCallback& cb)
    {
        _message_cb = cb;
    }
    /* 注册连接上有任意事件被触发的回调函数 */
    void SetEventCallback(const EventCallback& cb)
    {
        _event_cb = cb;
    }
    /* 移除对用户态连接管理的回调函数 */
    void SetSvrClosedCallback(const ClosedCallback& cb)
    {
        _svrclosed_cb = cb;
    }
    /* 设置通信连接的协议上下文 */
    void SetContext(const Any& context)
    {
        _context = context;
    }
    /* 获取通信连接的协议上下文 */
    Any* GetContext()
    {
        return &_context;
    }

    /* 以下接口都要保证在Connection对应的LoopThread中执行，以保证线程安全 */
    /* 建立用户态连接Connection前的准备工作 */
    void ConnectionEstablished()
    {
        _eventloop->RunTaskInLoopThread(std::bind(&Connection::ConnectionEstablishedInThreadLoop, this));
    }
    /* 发送数据——将数据写入到发送缓冲区，启动通信套接字的写事件监控 */
    void Send(const std::string& in)
    {
        _eventloop->RunTaskInLoopThread(std::bind(&Connection::SendInLoopThread, this, in));
    }
    /* 关闭连接——预关闭，先处理用户态缓冲区中需要被处理的数据，然后真正关闭连接 */
    void Close()
    {
        _eventloop->RunTaskInLoopThread(std::bind(&Connection::CloseInLoopThread, this));
    }
    /* 启动/关闭非活跃连接超时销毁功能 */
    void EnableIdleRelease(uint32_t timeout)
    {
        _eventloop->RunTaskInLoopThread(std::bind(&Connection::EnableIdleReleaseInLoopThread, this, timeout));
    }
    void DisableIdleRelease()
    {
        _eventloop->RunTaskInLoopThread(std::bind(&Connection::DisableIdleReleaseInLoopThread, this));
    }
    /* 切换支持的应用层协议 */
    void UpdateProtocol(const Any& context, 
                        const ConnectedCallback& connected_cb, 
                        const ClosedCallback& closed_cb,
                        const MessageCallback& message_cb,
                        const EventCallback& event_cb)
    {
        _eventloop->RunTaskInLoopThread(std::bind(&Connection::UpdateProtocolInThreadLoop, this, 
                                        context, connected_cb, closed_cb, message_cb, event_cb));
    }
};

#endif